package com.xjd.io.in;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <pre>
 * <b>事件InputStream</b>
 * 能够将InputStream的每一个操作都转化为事件通知监听对象，并可通过监听对象的结果对操作进行控制。
 * 
 * </pre>
 * 
 * @author elvis.xu
 * @version 2013-1-9 下午12:13:50
 */
public class EventInputStream extends FilterInputStream {
	public static final int READ = 1 << 0;
	public static final int SKIP = 1 << 1;
	public static final int CLOSE = 1 << 2;
	public static final int EXCEPTION = 1 << 3;

	protected int interestEventOp;
	protected List<EventInputStreamListener> listeners = null;

	public void addListener(EventInputStreamListener listener) {
		if (this.listeners == null) {
			this.listeners = new ArrayList<EventInputStreamListener>();
		}
		this.listeners.add(listener);
	}

	public void removeListener(EventInputStreamListener listener) {
		if (this.listeners != null) {
			this.listeners.remove(listener);
		}
	}

	public void clearListener() {
		this.listeners = null;
	}

	public EventInputStream(InputStream in) {
		this(in, null);
	}

	public EventInputStream(InputStream in, int interestEventOp) {
		this(in, interestEventOp, null);
	}

	public EventInputStream(InputStream in, EventInputStreamListener... listeners) {
		this(in, READ | SKIP | CLOSE, listeners);
	}

	public EventInputStream(InputStream in, int interestEventOp, EventInputStreamListener... listeners) {
		super(in);
		this.interestEventOp = interestEventOp;
		if (listeners != null) {
			this.listeners = new ArrayList<EventInputStreamListener>();
			this.listeners.addAll(Arrays.asList(listeners));
		}
	}

	@Override
	public int read() throws IOException {
		try {
			if (this.listeners != null && (this.interestEventOp & READ) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					listener.beforeRead(t);
				}
				int i = super.read();
				for (EventInputStreamListener listener : this.listeners) {
					i = listener.afterRead(t, i);
				}
				return i;
			} else {
				return super.read();
			}
		} catch (IOException e) {
			if (this.listeners != null && (this.interestEventOp & EXCEPTION) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					listener.onException(t, e);
				}
			}
			throw e;
		}
	}

	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		try {
			if (this.listeners != null && (this.interestEventOp & READ) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					if (!listener.beforeReadBytes(t)) {
						return 0;
					}
				}
				int i = super.read(b, off, len);
				for (EventInputStreamListener listener : this.listeners) {
					listener.afterReadBytes(t, b, off, i);
				}
				return i;
			} else {
				return super.read(b, off, len);
			}
		} catch (IOException e) {
			if (this.listeners != null && (this.interestEventOp & EXCEPTION) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					listener.onException(t, e);
				}
			}
			throw e;
		}
	}

	@Override
	public long skip(long n) throws IOException {
		try {
			if (this.listeners != null && (this.interestEventOp & SKIP) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					if (!listener.beforeSkip(t)) {
						return 0;
					}
				}
				long i = super.skip(n);
				for (EventInputStreamListener listener : this.listeners) {
					listener.afterSkip(t, i);
				}
				return i;
			} else {
				return super.skip(n);
			}
		} catch (IOException e) {
			if (this.listeners != null && (this.interestEventOp & EXCEPTION) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					listener.onException(t, e);
				}
			}
			throw e;
		}
	}

	@Override
	public void close() throws IOException {
		try {
			if (this.listeners != null && (this.interestEventOp & CLOSE) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					if (!listener.beforeClose(t)) {
						return;
					}
				}
				super.close();
				for (EventInputStreamListener listener : this.listeners) {
					listener.afterClose(t);
				}
			}
			super.close();
		} catch (IOException e) {
			if (this.listeners != null && (this.interestEventOp & EXCEPTION) != 0) {
				Thread t = Thread.currentThread();
				for (EventInputStreamListener listener : this.listeners) {
					listener.onException(t, e);
				}
			}
			throw e;
		}
	}

	public static interface EventInputStreamListener {

		void beforeRead(Thread useThread) throws IOException;

		int afterRead(Thread useThread, int readInt) throws IOException;

		boolean beforeReadBytes(Thread useThread) throws IOException;

		void afterReadBytes(Thread useThread, byte[] b, int off, int readLen) throws IOException;

		boolean beforeSkip(Thread useThread) throws IOException;

		void afterSkip(Thread useThread, long skipped) throws IOException;

		boolean beforeClose(Thread useThread) throws IOException;

		void afterClose(Thread useThread) throws IOException;

		void onException(Thread useThread, IOException e);
	}

	public static class EventInputStreamListenerAdapter implements EventInputStreamListener {

		@Override
		public void beforeRead(Thread useThread) throws IOException {
		}

		@Override
		public int afterRead(Thread useThread, int readInt) throws IOException {
			return readInt;
		}

		@Override
		public boolean beforeReadBytes(Thread useThread) throws IOException {
			return true;
		}

		@Override
		public void afterReadBytes(Thread useThread, byte[] b, int off, int readLen) throws IOException {
		}

		@Override
		public boolean beforeSkip(Thread useThread) throws IOException {
			return true;
		}

		@Override
		public void afterSkip(Thread useThread, long skipped) throws IOException {
		}

		@Override
		public boolean beforeClose(Thread useThread) throws IOException {
			return true;
		}

		@Override
		public void afterClose(Thread useThread) throws IOException {
		}

		@Override
		public void onException(Thread useThread, IOException e) {
		}
	}
}
